Release 10.1A: OpenEdge Development:
Progress 4GL Handbook


Making a forward declaration for a function

In Chapter 4, "Introducing the OpenEdge AppBuilder," you looked at the overall organization of a procedure file and used the AppBuilder-generated form as a good basis for your own procedure files. In fact, you should always use the AppBuilder to create your procedures if they have any internal entries at all, even if they have no visual content. The AppBuilder organizes the physical file for you and generates a lot of the supporting code you need.

If you follow the structural guidelines for a procedure file, you place all your internal entries—internal procedures and user-defined functions—at the end of the file. Normally, you need to reference the functions in your procedure file within the body of the code that comes before their actual implementation, either in the main block or in other internal entries in the file.

The Progress compiler needs to understand how to treat your function when it encounters it in the executable code. In particular, it needs to know the data type to return. It also does you a service by enforcing the rest of the function’s signature—its parameter list—whenever it finds a reference to it. For this reason, Progress needs to know at least the definition of the function—its return type and parameters—before it encounters a use of the function elsewhere in the procedure. To do this and still leave the actual implementation of the function toward the end of the file, you can provide a forward declaration of the function, also called a prototype, toward the top of the procedure file, before any code that uses the function.

This is the syntax for a forward declaration of a function:

FUNCTION function-name [ RETURNS ] datatype [ ( parameters ) 
  { FORWARD | [ MAP [ TO ] actual-name ] IN proc-handle | IN SUPER }  

As you can see, the basic definition of the function name, return type, and parameters is the same as you would use in the function header itself. If you provide a forward declaration for a function, the parameter list is optional in the function header (though the RETURNS phrase is not). It’s good practice, though, to provide it in both places.

A function prototype can point to an actual function implementation in one of three kinds of places:

Making a local forward declaration

If you use the FORWARD keyword in the function prototype, then this tells Progress to expect the actual function implementation later in the same procedure file. Here’s a simple example of a function that converts Celsius temperatures to Fahrenheit:

/* h-ConvTemp1.p -- procedure to convert temperatures 
   and demonstrate functions. */  
FUNCTION CtoF RETURNS DECIMAL (INPUT dCelsius AS DECIMAL) FORWARD. 
DEFINE VARIABLE dTemp AS DECIMAL    NO-UNDO. 
REPEAT dTemp = 0 TO 100: 
    DISPLAY dTemp LABEL "Celsius" 
            CtoF(dTemp) LABEL "Fahrenheit" 
            WITH FRAME f 10 DOWN. 
END. 
FUNCTION CtoF RETURNS DECIMAL (INPUT dCelsius AS DECIMAL): 
    RETURN (dCelsius * 1.8) + 32. 
END FUNCTION. 

This procedure executes as follows:

  1. The procedure makes a forward declaration of the CtoF conversion function, so that it can be used in the procedure before its implementation code is defined.
  2. The function is used inside the REPEAT loop in the DISPLAY statement. Notice that it appears where any DECIMAL expression could appear and is treated the same way.
  3. There is the actual implementation of the function, which takes the Celsius temperature as input and returns the Fahrenheit equivalent.

Figure 14–1 shows the first page of output from the h-ConvTemp1.p procedure.

Figure 14–1: Result of the ConvTemp.p procedure

You could leave the parameter list out of the function implementation itself, but it’s good form to leave it in.

Making a declaration of a function in another procedure

Because functions are so generally useful, you might want to execute a function from many procedures when it is in fact implemented in a single procedure file that your application runs persistent. In this case, you can provide a prototype that specifies the handle variable where the procedure handle of the other procedure is to be found at run time. This is the second option in the prototype syntax:

 [ MAP [ TO ] actual-name ] IN proc-handle 

The proc-handle is the name of the handle variable that holds the procedure handle where the function is actually implemented. If the function has a different name in that procedure than in the local procedure, you can provide the MAP TO actual-name phrase to describe this. In that case, actual-name is the function name in the procedure whose handle is proc-handle.

To see an example of the CtoF function separated out in this way:

  1. Create a procedure with just the function definition in it:
  2. /* h-FuncProc.p -- contains CtoF and possible other useful functions. */ 
    FUNCTION CtoF RETURNS DECIMAL (INPUT dCelsius AS DECIMAL): 
         RETURN (dCelsius * 1.8) + 32. 
    END FUNCTION. 
    

    It could also have other internal entries to be used by others that have its procedure handle at run time.

  3. Change the procedure that uses the function to declare it IN hFuncProc, its procedure handle. The code has to run h-FuncProc.p persistent or else access its handle if it’s already running. In this case, it also deletes it when it’s done:
  4. /* h-ConvTemp2.p -- procedure to convert temperatures 
       and demonstrate functions. */  
    DEFINE VARIABLE hFuncProc AS HANDLE     NO-UNDO. 
    FUNCTION CtoF RETURNS DECIMAL (INPUT dCelsius AS DECIMAL) IN hFuncProc. 
    DEFINE VARIABLE dTemp AS DECIMAL      NO-UNDO. 
    RUN h-FuncProc.p PERSISTENT SET hFuncProc. 
    REPEAT dTemp = 0 TO 100: 
        DISPLAY dTemp LABEL "Celsius" 
                CtoF(dTemp) LABEL "Fahrenheit" 
                WITH FRAME f 10 DOWN. 
    END. 
    DELETE PROCEDURE hFuncProc. 
    

  5. Run this variant h-ConvTemp2.p, to get the same result as before. This time here’s the end of the display, just to confirm that you got the arithmetic right:

An externally defined function such as this one can also reside in an entirely different OpenEdge session, connected to the procedure that uses the function using the OpenEdge AppServer. In this case, you declare the function in the same way but use the AppServer-specific ON SERVER phrase on the RUN statement to invoke the function. As with any AppServer call, you can’t pass a buffer as a parameter to such a function. See OpenEdge Application Server: Developing AppServer Applications for more information.

Making a declaration of a function IN SUPER

The third option in the function prototype is to declare that it is found IN SUPER at run time. This means that the function is implemented in a super procedure of the procedure with the declaration. You learn about super procedures in the "Using super procedures in your application" section.


Copyright © 2005 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095